package com.piece.module.file.util.word;

import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.piece.core.framework.util.collection.MapUtil;
import com.piece.core.framework.util.math.SequenceUtil;
import fr.opensagres.poi.xwpf.converter.xhtml.Base64EmbedImgManager;
import fr.opensagres.poi.xwpf.converter.xhtml.XHTMLConverter;
import fr.opensagres.poi.xwpf.converter.xhtml.XHTMLOptions;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.converter.WordToHtmlConverter;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTAnchor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.w3c.dom.Document;
import javax.xml.bind.DatatypeConverter;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Word文档工具类
 */
@Slf4j
public class WordUtil {

    /**
     * 根据docx文档模板生成文件流
     *
     * @param templatePath docx模板文件的路径（不含文件名）
     * @param templateName docx模板文件的名称（不含路径）
     * @param data         模型数据
     * @param os
     * @throws
     */
    @Deprecated
    public static void generateByTemplate(String templatePath, String templateName, Map<String, Object> data, OutputStream os) throws Exception {
        generateByTemplate(templatePath, templateName, data, null, os);
    }

    /**
     * 根据docx文档模板生成文件流
     *
     * @param templatePath docx模板文件的路径（不含文件名）
     * @param templateName docx模板文件的名称（不含路径）
     * @param data         模型数据
     * @param tableItem    列表数据
     * @param os
     * @throws
     */
    @Deprecated
    public static void generateByTemplate(String templatePath, String templateName, Map<String, Object> data, List<String> tableItem, OutputStream os) throws Exception {
        InputStream inputStream = ClassLoader.getSystemResourceAsStream(templatePath + File.separator + templateName);
        generateByInputStream(inputStream, data, tableItem, os);
    }

    /**
     * 根据docx文档模板生成文件流
     *
     * @param inputStream 模板流数据
     * @param data        模型数据
     * @param os          输出流
     * @throws
     */
    public static void generateByInputStream(InputStream inputStream, Map<String, Object> data, OutputStream os) throws Exception {
        generateByInputStream(inputStream, data, null, os);
    }

    /**
     * 根据docx文档模板生成文件流
     *
     * @param inputStream 模板流数据
     * @param data        模型数据
     * @param tableItem   列表数据
     * @param os          输出流
     * @throws
     */
    public static void generateByInputStream(InputStream inputStream, Map<String, Object> data, List<String> tableItem, OutputStream os) throws Exception {
        Configure configure = null;
        if (null != tableItem) {
            LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
            ConfigureBuilder builder = Configure.builder();
            tableItem.stream().forEach(key -> builder.bind(key, policy));
            configure = builder.build();
        }

        XWPFTemplate template;
        if (null != configure) {
            template = XWPFTemplate.compile(inputStream, configure);
        } else {
            template = XWPFTemplate.compile(inputStream);
        }

        if (MapUtil.isNotEmpty(data)) {
            template = template.render(data);
        }
        template.write(os);
    }

    /**
     * doc文件转换Html
     *
     * @param is doc文件流
     */
    public static String docToHtml(InputStream is) throws Exception {
        HWPFDocument wordDocument = new HWPFDocument(is);
        WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument());
        wordToHtmlConverter.setPicturesManager((content, pictureType, suggestedName, widthInches, heightInches) -> {
            String base64 = DatatypeConverter.printBase64Binary(content);
            return "data:images/" + pictureType.getExtension() + ";base64," + base64;
        });
        wordToHtmlConverter.processDocument(wordDocument);
        Document htmlDocument = wordToHtmlConverter.getDocument();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        DOMSource domSource = new DOMSource(htmlDocument);
        StreamResult streamResult = new StreamResult(out);

        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer serializer = tf.newTransformer();
        serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
        serializer.setOutputProperty(OutputKeys.INDENT, "yes");
        serializer.setOutputProperty(OutputKeys.METHOD, "html");
        serializer.transform(domSource, streamResult);
        out.flush();
        return new String(out.toByteArray());
    }

    /**
     * docx文件转换Html
     *
     * @param is docx文件流
     */
    public static String docxToHtml(InputStream is) throws Exception {
        XWPFDocument document = new XWPFDocument(is);
        XHTMLOptions options = XHTMLOptions.create();
        options.setImageManager(new Base64EmbedImgManager());
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        XHTMLConverter xhtmlConverter = (XHTMLConverter) XHTMLConverter.getInstance();
        xhtmlConverter.convert(document, out, options);
        out.flush();
        return new String(out.toByteArray());
    }

    public static ByteArrayOutputStream sealDocument(InputStream is, Map<String, XWPFPicture> seals) throws Exception {
        XWPFDocument doc = new XWPFDocument(is);
        XWPFParagraph currentParagraph;
        XWPFPicture seal;
        String key, text;
        ByteArrayOutputStream os = new ByteArrayOutputStream();

        List<XWPFParagraph> xwpfParagraphList = doc.getParagraphs();
        List<String> keys = seals.keySet().stream().collect(Collectors.toList());
        for (int i = 0; i < xwpfParagraphList.size(); i++) {
            currentParagraph = null;
            key = null;
            text = xwpfParagraphList.get(i).getText();
            for (String key_ : keys) {
                if (text.indexOf(key_) != -1) {
                    key = key_;
                    currentParagraph = xwpfParagraphList.get(i);
                    break;
                }
            }

            if (null == currentParagraph) {
                continue;
            }

            seal = seals.get(key);
            if (null != seal) {
                //添加印章图片
                XWPFRun run = currentParagraph.createRun();
                run.addPicture(seal.getInputStream(), seal.getType().type(), seal.getFilename(), Units.toEMU(seal.getWidth()), Units.toEMU(seal.getHeight()));
                seal.getInputStream().close();
                CTR ctr = run.getCTR();
                if (ctr.sizeOfDrawingArray() > 0) {
                    CTDrawing drawing = ctr.getDrawingArray(0);
                    if (drawing.sizeOfInlineArray() > 0) {
                        CTGraphicalObject graphical = drawing.getInlineArray(0).getGraphic();
                        //设置图片位置
                        CTAnchor anchor = getAnchorWithGraphic(graphical, "Seal" + SequenceUtil.next(),
                                Units.toEMU(seal.getWidth()), Units.toEMU(seal.getHeight()),
                                Units.toEMU(seal.getOffsetWidth()), Units.toEMU(seal.getOffsetHeight()), false);
                        //添加浮动属性
                        drawing.setAnchorArray(new CTAnchor[]{anchor});
                        //删除行内属性
                        drawing.removeInline(0);
                    }
                }
                seal = null;
            }
        }

        doc.write(os);
        is.close();
        doc.close();
        return os;
    }

    public static CTAnchor getAnchorWithGraphic(CTGraphicalObject ctGraphicalObject,
                                                String deskFileName, int width, int height,
                                                int leftOffset, int topOffset, boolean behind) {
        String anchorXML =
                "<wp:anchor xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" "
                        + "simplePos=\"0\" relativeHeight=\"0\" behindDoc=\"" + ((behind) ? 1 : 0) + "\" locked=\"0\" layoutInCell=\"1\" allowOverlap=\"1\">"
                        + "<wp:simplePos x=\"0\" y=\"0\"/>"
                        + "<wp:positionH relativeFrom=\"column\">"
                        + "<wp:posOffset>" + leftOffset + "</wp:posOffset>"
                        + "</wp:positionH>"
                        + "<wp:positionV relativeFrom=\"paragraph\">"
                        + "<wp:posOffset>" + topOffset + "</wp:posOffset>" +
                        "</wp:positionV>"
                        + "<wp:extent cx=\"" + width + "\" cy=\"" + height + "\"/>"
                        + "<wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>"
                        + "<wp:wrapNone/>"
                        + "<wp:docPr id=\"1\" name=\"Drawing 0\" descr=\"" + deskFileName + "\"/><wp:cNvGraphicFramePr/>"
                        + "</wp:anchor>";

        CTDrawing drawing = null;
        try {
            drawing = CTDrawing.Factory.parse(anchorXML);
        } catch (Exception e) {}
        CTAnchor anchor = drawing.getAnchorArray(0);
        anchor.setGraphic(ctGraphicalObject);
        return anchor;
    }
}
